package cc.chengheng.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * 请求获取授权码
 * http://127.0.0.1:9001/oauth/authorize?client_id=fengliulin&response_type=code
 *
 * http://127.0.0.1:9001/oauth/authorize?client_id=fengliulin&response_type=token
 * https://www.xxx.com/#access_token=b996b557-ca86-4607-8ed9-a087a264c6e9&token_type=bearer&expires_in=40779&scope=read%20write#access_token=b996b557-ca86-4607-8ed9-a087a264c6e9&token_type=bearer&expires_in=40779&scope=read%20write
 */
@Configuration
@EnableAuthorizationServer
public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter {


    @Resource
    private DataSource dataSource;

    /** 认证业务对象，就是UserDetailsServiceImpl */
    @Resource
    private UserDetailsService userDetailsService;

    /** 授权码模式专用对象 AuthenticationManagerDelegator */
    @Resource
    private AuthenticationManager authenticationManagerBean;

    // token保存策略
    @Bean
    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource); // 将令牌存储在数据库中的令牌服务的实现
    }

    // 授权信息保存策略
    @Bean
    public ApprovalStore approvalStore() {
        return new JdbcApprovalStore(dataSource);
    }

    // 授权码模式数据来源
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource); // 授权码的实现类
    }

    /**
     * 客户端信息来源, 链接数据库，查询出来
     */
    @Bean
    public JdbcClientDetailsService jdbcClientDetailsService() {
        return new JdbcClientDetailsService(dataSource); // 基本的，客户端详细信息服务的 JDBC 实现。
    }

    /**
     * 指定客户端信息的数据来源
     * client_id 会自动到数据去查询那一条信息
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(jdbcClientDetailsService());
    }

    /**
     * 检测token的策略
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 固定格式写法
        security.allowFormAuthenticationForClients();
        security.checkTokenAccess("isAuthenticated()"); // 检查已经认证的
    }

    /**
     * oauth2的主配置信息，主要让上面的配置综合进来
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .userDetailsService(userDetailsService) // 验证用户是否认证，好刷新token
                .approvalStore(approvalStore())
                .authenticationManager(authenticationManagerBean) // 认证管理器
                .authorizationCodeServices(authorizationCodeServices()) // 授权码服务
                .tokenStore(tokenStore());
    }

}
